#pragma once

#include<iostream>
#include<cstring>
#include<filesystem>//c++17
#include<fstream>
#include<sstream>
#include<ctime>
#include<unistd.h>
#include<memory>
#include"Mutex.hpp"
namespace LogModule
{
    using namespace MuteModule;
    #define gsep "\r\n"
    //定义刷新策略的基类  Strategy策略
    class LogStrategy
    {
    //父类的函数一定是公有的才能被子类访问
    public:
        ~LogStrategy() = default;
        //sync同步
        virtual void SyncLog(const std::string& message) = 0;//定义了一个纯虚函数
    };

    //刷新到显示器上的刷新策略  console控制台
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        ConsoleLogStrategy()
        {}
        void SyncLog(const std::string& message) override
        {
            LockGuard t(_mutex);//加锁保证像显示器上写的数据是原子性的(构造函数时没有初始化锁)
            std::cout << message << gsep;//直接将数据刷新到显示器上
        }
        ~ConsoleLogStrategy()
        {

        }
    private:
        Mutex _mutex;
    };

    #define defaultpath "./log"
    #define defaultfile "mylog.log"
    //刷新到文件上的策略
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string& path = defaultpath,const std::string& file = defaultfile)
        :_path(path),_file(file)
        {
            //要先判断路径是否存在,不存在要创建
            if(std::filesystem::exists(_path))//查看路径是否存在
            {
                return ;//存在什么也不做
            }
            //不存在说明要创建
            LockGuard t(_mutex);//创建文件的操作也要保证是原子性的(有疑问,为什么匿名对象在这里不可用)
            try
            {
                std::filesystem::create_directories(_path);//创建目录
            }
            catch(const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << gsep;
            }
            
        }

        void SyncLog(const std::string& message) override
        {
            LockGuard t(_mutex);//加锁保证像显示器上写的数据是原子性的
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(filename,std::ios::app);
            if(!out.is_open()){ 
                std::cout << "文件打开失败,刷新失败" << std::endl;
                return; 
            }//未打开自己接返回

            //打开就将数据插入到文件里

            out << message << gsep;
        }
        ~FileLogStrategy()
        {

        }
    private:
        std::string _path;
        std::string _file;
        Mutex _mutex;
    };


    //日历等级制度
    enum class LogLeve1
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    //拿到日志等级
    std::string LogLeve12str(LogLeve1 log)
    {
        switch(log)
        {
            case LogLeve1::DEBUG:
                return "DEBUG";
            case LogLeve1::INFO:
                return "INFO";
            case LogLeve1::ERROR:
                return "ERROR";
            case LogLeve1::WARNING:
                return "WARNING";
            default:
                return "FATAL";
        }
        return "";//给编译器看的
    }

    //拿到当前时间
    std::string GetTime()
    {
        time_t curr = time(nullptr);
        struct tm curr_tm;
        localtime_r(&curr,&curr_tm);//拿到当前时间的属性
        char timebuffer[128] = {0};
        //将拿到的属性格式化成字符串
        snprintf(timebuffer,sizeof(timebuffer),"%4d-%02d-%02d  %02d:%02d:%02d",
                 curr_tm.tm_year + 1900,
                 curr_tm.tm_mon + 1,
                 curr_tm.tm_mday,
                 curr_tm.tm_hour,
                 curr_tm.tm_min,
                 curr_tm.tm_sec
        );
    return timebuffer;
    }

    //形成日志
    class Logger
    {
    public:
        Logger()
        {
            //开始就要有一种刷新策略
            EnableFileLogStrategy();//默认刷新到文件中
        }
        //文件刷新
        void EnableFileLogStrategy()
        {
            _fflush_strategy = std::make_unique<FileLogStrategy>();
        }
        //显示器刷新
        void EnableConsoleLogStrategy()
        {
            _fflush_strategy = std::make_unique<ConsoleLogStrategy>();
        }

        //形成一条日志信息
        class LogMessage
        {
        public:
            LogMessage(LogLeve1 LogLever_level,const std::string& name,int Line_number,Logger& logger)
            :_crr_time(GetTime()),
             _LogLever_level(LogLever_level),
             _pid(getpid()),
             _src_name(name),
             _Line_number(Line_number),
             _logger(logger)
            {
                //合成日志的左半部分
                std::stringstream so;
                so << "[" << _crr_time << "] " 
                   << "[" << LogLeve12str(_LogLever_level) << "] "
                   << "[" << _pid << "] "
                   << "[" << _src_name << "] "
                   << "[" << _Line_number << "]  "
                   << "-";
                _loginfo += so.str();
            }

            //右半部分递归获取数据
            template<class T>
            LogMessage& operator<<(const T& t)
            {
                std::stringstream so;
                so << t;
                _loginfo += so.str();
                return *this;
            }
            ~LogMessage()
            {
                _logger._fflush_strategy->SyncLog(_loginfo);//将数据刷新到指定文件中
            }
        private:
            std::string _crr_time;//时间
            LogLeve1 _LogLever_level;//日志等级
            pid_t _pid;
            std::string _src_name;//文件名
            int _Line_number;//行号
            std::string _loginfo;//最终日志
            Logger& _logger;//外部类的实例,用来刷新数据
        };

        LogMessage operator()(LogLeve1 LogLever_level,const std::string& name,int Line_number)
        {
            return LogMessage(LogLever_level,name,Line_number,*this);
        }
    private:
        std::unique_ptr<LogStrategy> _fflush_strategy;
    };
    // 全局日志对象
    Logger logger;

    // 使用宏，简化用户操作，获取文件名和行号
    #define LOG(level) logger(level, __FILE__, __LINE__)
    #define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
    #define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}